/*
  CRMA Sensor Network Project 2011
 http://siltc.crma.ac.th
 Writen by pyavinci@gmail.com
(Note: Not originally coded by me. Some parts of code available 
       via Arduino website and vender's provided libraries)

 For Hardware: Mega1280 + EtherShield(Mega) 
  *LCD2004 (20x4)
  * DS18S20: OneWire temperature
  * Light sensor
  * SHT1x: Temperature&Humidity module
 CHANGE LOG
   June7, 2011: Added: SHT1x Temperature and Humidity module
   * Too much delay to the web service when added other components
   * Ping responses are partially through
  NOTE!!!! on EtherShield for Mega
  * Digital pins occupied: 22,23,24, 50-53
  * Replace directory "ethershield" with one from eKitsZone in
    "libraries" directory --> exit and start Arduino to use it
  Jun8,2011: Added Ultrasonic Ranging
    
 CONTACT INFO: pyavinci@gmail.com, prachya@crma.ac.th
 */
#define NODE_ID 001
#include "etherShield.h"
#include "stdio.h"
#include <OneWire.h>
#include <LiquidCrystal.h>
#include <SHT1x.h>
#define dataPin  31   // DATA for SHT1x
#define clockPin 33   // SCK for SHT1x
// LCD=======================================================
#define LCD_WIDTH 20
#define LCD_HEIGHT 4
#define LD4 11
#define LD5 10
#define LD6 9
#define LD7 8
#define LEN 12       // LCD Enable
#define LRS 13       // LCD RS
/*=================+======+
  LCD 16x2           Mega
  1 (Vss/Ground)     GND
  2 VCC              +5V
  3 Vo(contrast)     VR
  4 RS               13
  5 R/W              GND
  6 Enable           12
  Pin 11-14 (D4-D7)  11-8
*/
#define BUFFER_SIZE 500
#define STR_BUFFER_SIZE 22
#define MAX_DS1820_SENSORS 1
#define TEMP_PIN  29
//----- Ultrasonic Ranging -----
#define USPING 35
#define USPONG 37


// please modify the following two lines. mac and ip have to be unique
// in your local area network. You can not have the same numbers in
// two devices:
static uint8_t mymac[6] = {
  0x54,0x55,0x58,0x10,0x00,0x24}; 
static uint8_t myip[4] = {
  192,168,1,15};
static char baseurl[]="http://192.168.1.15/";
// listen port for tcp/www (max range 1-254)
static uint16_t mywwwport =80;  
static uint8_t buf[BUFFER_SIZE+1];
static char strbuf[STR_BUFFER_SIZE+1];

// prepare the webpage by writing the data to the tcp send buffer
uint16_t print_webpage(uint8_t *buf);
int8_t analyse_cmd(char *str);

void ES_Setup(void);

////// O B J E C T S ///////
LiquidCrystal lcd(LRS, LEN, LD4, LD5, LD6, LD7);
EtherShield es=EtherShield();
OneWire  ds(TEMP_PIN);  // pin that DS18S20 attached to
SHT1x sht1x(dataPin, clockPin); // Temp-Humid

///// GLOBAL VARIABLES 
int reliaCount=0;
long prevMil=0;
int HighByte, LowByte, TReading, SignBit, Tc_100, Whole, Fract;
char tbuf[20];
byte addr[MAX_DS1820_SENSORS][8];

void setup(){
  lcd.begin(LCD_WIDTH, LCD_HEIGHT,1);
  lcd.setCursor(0,0);
  lcd.print("CRMA SensNet");

  /* Disable SD card */
  pinMode(4, OUTPUT);
  digitalWrite(4, HIGH);

  Serial.begin(9600);

  /*initialize enc28j60*/
  es.ES_enc28j60Init(mymac);
  es.ES_enc28j60clkout(2); // change clkout from 6.25MHz to 12.5MHz
  delay(10);
  ES_Setup();

  //init the ethernet/ip layer:
  es.ES_init_ip_arp_udp_tcp(mymac,myip,80);

  //////  DS18S20 ////////p;lop
  lcd.setCursor(0,3);
  lcd.print("Searching DS18S20...");
  if (!ds.search(addr[0])) 
  {
    Serial.println("No more addresses.");
    ds.reset_search();
    delay(250);
    return;
  }
  if ( !ds.search(addr[1])) 
  {
    Serial.println("No more addresses.");
    ds.reset_search();
    delay(250);
    return;
  }


}


////////// M A I N  L O O P ///////////////
void loop(){
  uint16_t plen, dat_p;
  int8_t cmd;
  float temp_c;
  float temp_f;
  float humidity;

  ///////////// for HC-SR04 Ultrasonic Ranging /////
  // establish variables for duration of the ping, 
  // and the distance result in inches and centimeters:
  long duration, inches, cm;
  // The PING))) is triggered by a HIGH pulse of 2 or more microseconds.
  // Give a short LOW pulse beforehand to ensure a clean HIGH pulse:
  pinMode(USPING, OUTPUT);
  digitalWrite(USPING, LOW);
  delayMicroseconds(2);
  digitalWrite(USPING, HIGH);
  delayMicroseconds(10);
  digitalWrite(USPING, LOW);

  // The same pin is used to read the signal from the PING))): a HIGH
  // pulse whose duration is the time (in microseconds) from the sending
  // of the ping to the reception of its echo off of an object.
  pinMode(USPONG, INPUT);
  duration = pulseIn(USPONG, HIGH);

  // convert the time into a distance
  //inches = microsecondsToInches(duration);
  cm = microsecondsToCentimeters(duration);
  lcd.setCursor(0,2); lcd.print("                   ");
  lcd.setCursor(0,2); lcd.print(cm); lcd.print(" cm");


  // Read values from the SHT1 sensor
  temp_c = sht1x.readTemperatureC();
  temp_f = sht1x.readTemperatureF();
  humidity = sht1x.readHumidity();


  /// FOR DS18S20
  byte i, sensor;
  byte present = 0;
  byte data[12];

  // LOOP FOR MULTIPLE DS18S20(s)
  for (sensor=0;sensor<MAX_DS1820_SENSORS;sensor++)
  {
    if ( OneWire::crc8( addr[sensor], 7) != addr[sensor][7]) 
    {
      Serial.print("CRC is not valid");
      return;
    }

    if ( addr[sensor][0] != 0x10) 
    {
      Serial.println("Device is not a DS18S20 family device.");
      return;
    }

    ds.reset();
    ds.select(addr[sensor]);
    ds.write(0x44,1);         // start conversion, with parasite power on at the end

    delay(750);     // maybe 750ms is enough, maybe not
    // we might do a ds.depower() here, but the reset will take care of it.

    present = ds.reset();
    ds.select(addr[sensor]);    
    ds.write(0xBE);         // Read Scratchpad

    for ( i = 0; i < 9; i++) 
    {           // we need 9 bytes
      data[i] = ds.read();
    }

    LowByte = data[0];
    HighByte = data[1];
    TReading = (HighByte << 8) + LowByte;
    SignBit = TReading & 0x8000;  // test most sig bit
    if (SignBit) // negative
    {
      TReading = (TReading ^ 0xffff) + 1; // 2's comp
    }
    Tc_100 = (TReading*100/2);    

    Whole = Tc_100 / 100;  // separate off the whole and fractional portions
    Fract = Tc_100 % 100;

    sprintf(tbuf, "%d.%d",Whole, Fract < 10 ? 0 : Fract);
  }

  // send out to serial about every second, sensor information
  // attached to this node
  if(millis()-prevMil > 1000) {
    Serial.print("Node-"); 
    Serial.print(NODE_ID,DEC);
    Serial.print("(");
    Serial.print(reliaCount++, DEC); 
    Serial.print("): <");
    for (int i = 0; i < 1; i++)
    {
      Serial.print(analogRead(i)); 
      Serial.print(" ");
    }
    Serial.print(tbuf);    Serial.print(" "); 
    Serial.print(temp_c);    Serial.print(" "); 
    Serial.print(humidity);    Serial.print(" ");
    Serial.print(cm);   Serial.println(">");
    prevMil=millis();
  }

  //////  LCD OUTPUT //////
  lcd.setCursor(0,0); lcd.print("T1       H      ");
  lcd.setCursor(3,0); lcd.print(temp_c);
  lcd.setCursor(11,0); lcd.print(humidity);
  lcd.setCursor(0,1); lcd.print("T2       L      ");
  long ls=analogRead(0);
  lcd.setCursor(3,1);
  lcd.print(tbuf); // Temperature from DS18S20
  lcd.setCursor(11,1);
  lcd.print(ls); // Light Sensor data

  ///////// TCP/IP PART
  plen = es.ES_enc28j60PacketReceive(BUFFER_SIZE, buf);

  /*plen will ne unequal to zero if there is a valid packet (without crc error) */
  if(plen!=0){

    // arp is broadcast if unknown but a host may also verify the mac address by sending it to a unicast address.
    if(es.ES_eth_type_is_arp_and_my_ip(buf,plen)){
      es.ES_make_arp_answer_from_request(buf);
      return;
    }

    // check if ip packets are for us:
    if(es.ES_eth_type_is_ip_and_my_ip(buf,plen)==0){
      return;
    }

    if(buf[IP_PROTO_P]==IP_PROTO_ICMP_V && buf[ICMP_TYPE_P]==ICMP_TYPE_ECHOREQUEST_V){
      es.ES_make_echo_reply_from_request(buf,plen);
      return;
    }

    // tcp port www start, compare only the lower byte
    if (buf[IP_PROTO_P]==IP_PROTO_TCP_V&&buf[TCP_DST_PORT_H_P]==0&&buf[TCP_DST_PORT_L_P]==mywwwport){
      if (buf[TCP_FLAGS_P] & TCP_FLAGS_SYN_V){
        es.ES_make_tcp_synack_from_syn(buf); // make_tcp_synack_from_syn does already send the syn,ack
        return;     
      }
      if (buf[TCP_FLAGS_P] & TCP_FLAGS_ACK_V){
        es.ES_init_len_info(buf); // init some data structures
        dat_p=es.ES_get_tcp_data_pointer();
        if (dat_p==0){ // we can possibly have no data, just ack:
          if (buf[TCP_FLAGS_P] & TCP_FLAGS_FIN_V){
            es.ES_make_tcp_ack_from_any(buf);
          }
          return;
        }
        if (strncmp("GET ",(char *)&(buf[dat_p]),4)!=0){
          // head, post and other methods for possible status codes see:
          // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
          plen=es.ES_fill_tcp_data_p(buf,0,PSTR("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>200 OK</h1>"));
          goto SENDTCP;
        }
        if (strncmp("/ ",(char *)&(buf[dat_p+4]),2)==0){
          plen=print_webpage(buf);
          goto SENDTCP;
        }
        cmd=analyse_cmd((char *)&(buf[dat_p+5]));
        if (cmd==1){
          plen=print_webpage(buf);
        }
SENDTCP:  
        es.ES_make_tcp_ack_from_any(buf); // send ack for http get
        es.ES_make_tcp_ack_with_data(buf,plen); // send data       
      }
    }
  }

}

/////////////////////////////////////////////////////////
// The returned value is stored in the global var strbuf
uint8_t find_key_val(char *str,char *key)
{
  uint8_t found=0;
  uint8_t i=0;
  char *kp;
  kp=key;
  while(*str &&  *str!=' ' && found==0){
    if (*str == *kp){
      kp++;
      if (*kp == '\0'){
        str++;
        kp=key;
        if (*str == '='){
          found=1;
        }
      }
    }
    else{
      kp=key;
    }
    str++;
  }
  if (found==1){
    // copy the value to a buffer and terminate it with '\0'
    while(*str &&  *str!=' ' && *str!='&' && i<STR_BUFFER_SIZE){
      strbuf[i]=*str;
      i++;
      str++;
    }
    strbuf[i]='\0';
  }
  return(found);
}

int8_t analyse_cmd(char *str)
{
  int8_t r=-1;

  if (find_key_val(str,"cmd")){
    if (*strbuf < 0x3a && *strbuf > 0x2f){
      // is a ASCII number, return it
      r=(*strbuf-0x30);
    }
  }
  return r;
}


uint16_t print_webpage(uint8_t *buf)
{
  char temp_string[10];
  int i=0;
  //char *temp_string="100";

  uint16_t plen;

  //getCurrentTemp(temp_string);

  plen=es.ES_fill_tcp_data_p(buf,0,PSTR("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"));
  plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<center><p><h1>CRMA Sensor Node: 001</h1></p> "));
  plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<h2> Current Temperature is </h2> "));
  while (tbuf[i]) {
    buf[TCP_CHECKSUM_L_P+3+plen]=tbuf[i++];
    plen++;
  }
  plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("</center><hr> <p> V1.0 <a href=\"http://siltc.crma.ac.th<a>"));

  return(plen);
}


void ES_Setup(void) {
  /* Magjack leds configuration, see enc28j60 datasheet, page 11 */
  // LEDA=greed LEDB=yellow
  //
  // 0x880 is PHLCON LEDB=on, LEDA=on
  // enc28j60PhyWrite(PHLCON,0b0000 1000 1000 00 00);
  es.ES_enc28j60PhyWrite(PHLCON,0x880);
  delay(500);
  //
  // 0x990 is PHLCON LEDB=off, LEDA=off
  // enc28j60PhyWrite(PHLCON,0b0000 1001 1001 00 00);
  es.ES_enc28j60PhyWrite(PHLCON,0x990);
  delay(500);
  //
  // 0x880 is PHLCON LEDB=on, LEDA=on
  // enc28j60PhyWrite(PHLCON,0b0000 1000 1000 00 00);
  es.ES_enc28j60PhyWrite(PHLCON,0x880);
  delay(500);
  //
  // 0x990 is PHLCON LEDB=off, LEDA=off
  // enc28j60PhyWrite(PHLCON,0b0000 1001 1001 00 00);
  es.ES_enc28j60PhyWrite(PHLCON,0x990);
  delay(500);
  //
  // 0x476 is PHLCON LEDA=links status, LEDB=receive/transmit
  // enc28j60PhyWrite(PHLCON,0b0000 0100 0111 01 10);
  es.ES_enc28j60PhyWrite(PHLCON,0x476);
  delay(100);

}

//// Routines for HC-SR04 Ultrasonic Ranging
long microsecondsToInches(long microseconds)
{
  // According to Parallax's datasheet for the PING))), there are
  // 73.746 microseconds per inch (i.e. sound travels at 1130 feet per
  // second).  This gives the distance travelled by the ping, outbound
  // and return, so we divide by 2 to get the distance of the obstacle.
  // See: http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf
  return microseconds / 74 / 2;
}

long microsecondsToCentimeters(long microseconds)
{
  // The speed of sound is 340 m/s or 29 microseconds per centimeter.
  // The ping travels out and back, so to find the distance of the
  // object we take half of the distance travelled.
  return microseconds / 29 / 2;
}



